package net.sk.china.front.service.impl;

import cn.hutool.core.util.ObjectUtil;
import net.sk.china.common.constants.Constants;
import net.sk.china.common.constants.PayConstants;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.model.order.StoreOrder;
import net.sk.china.common.model.user.User;
import net.sk.china.common.model.user.UserToken;
import net.sk.china.common.utils.DateUtil;
import net.sk.china.common.utils.RedisUtil;
import net.sk.china.common.utils.TalkUtil;
import net.sk.china.common.vo.WxPayJsResultVo;
import net.sk.china.front.dao.order.StoreOrderDao;
import net.sk.china.front.request.PayRequest;
import net.sk.china.front.request.order.OrderPayRequest;
import net.sk.china.front.response.PayResultResponse;
import net.sk.china.front.response.order.OrderPayResultResponse;
import net.sk.china.front.service.PayService;
import net.sk.china.front.service.user.UserService;
import net.sk.china.front.service.user.UserTokenService;
import net.sk.china.front.service.wechat.WeChatPayService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Map;

/**
 *
 * PayServiceImpl 接口实现
 * +----------------------------------------------------------------------
 * | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2018-2024 聊来All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * +----------------------------------------------------------------------
 * | @Author: 聊来 <18970881148@qq.com>
 * +----------------------------------------------------------------------
 * | DateTime: 2024/6/4 17:38
 * +----------------------------------------------------------------------
 */

@Service
public class PayServiceImpl implements PayService {

    @Resource
    private StoreOrderDao storeOrderDao;

    private final UserService userService;

    private final UserTokenService userTokenService;

    private final WeChatPayService weChatPayService;

    private final TransactionTemplate transactionTemplate;

    private RedisUtil redisUtil;

    public PayServiceImpl(UserService userService, UserTokenService userTokenService, WeChatPayService weChatPayService, TransactionTemplate transactionTemplate, RedisUtil redisUtil) {
        this.userService = userService;
        this.userTokenService = userTokenService;
        this.weChatPayService = weChatPayService;
        this.transactionTemplate = transactionTemplate;
        this.redisUtil = redisUtil;
    }

    /**
     * 微信支付范例
     * @param request 请求对象
     * @return {@code PayResultResponse}
     */
    @Override
    public PayResultResponse demo(PayRequest request) {
        User user = userService.getUserInfo();

        UserToken userToken = new UserToken();

        // 公众号
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_PUBLIC)) {
            userToken = userTokenService.getTokenByUserId(user.getUid(), 1);
        }
        // 小程序
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_PROGRAM)) {
            userToken = userTokenService.getTokenByUserId(user.getUid(), 2);
        }

        // H5、微信App支付ios、微信App支付android
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_H5) || request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_APP)) {
            userToken.setToken("");
        }

        String openId = userToken.getToken();
        String orderId = TalkUtil.getOrderNo("100");

        Map<String, String> map = weChatPayService.wxPay(user.getUid(), openId, request.getPayType(), orderId, new BigDecimal("0.01"), request.getClientIp(), "demo");

        WxPayJsResultVo wxPayConfig = new WxPayJsResultVo();
        wxPayConfig.setAppId(map.get("appId"));
        wxPayConfig.setNonceStr(map.get("nonceStr"));
        wxPayConfig.setPackages(map.get("package"));
        wxPayConfig.setSignType(map.get("signType"));
        wxPayConfig.setTimeStamp(map.get("timeStamp"));
        wxPayConfig.setPaySign(map.get("paySign"));
        wxPayConfig.setPrepayId(map.get("prepayid"));
        wxPayConfig.setMchId(map.get("partnerid"));
        wxPayConfig.setWebUrl(map.get("web_url"));

        PayResultResponse response = new PayResultResponse();

        // H5唤醒微信支付
        if (request.getPayType().equals(Constants.PAY_CHANNEL_WE_CHAT_H5)) {
            response.setPayType(Constants.PAY_CHANNEL_WE_CHAT_H5);
        }
        response.setWxPayConfig(wxPayConfig);
        response.setOrderNo(orderId);
        response.setPayType(request.getPayType());

        return response;
    }

    /**
     * 订单支付
     * @param orderPayRequest 支付参数
     * @param ip 客户端IP
     * @return {@code OrderPayResultResponse}
     */
    @Override
    public OrderPayResultResponse payment(OrderPayRequest orderPayRequest, String ip) {
        StoreOrder storeOrder = storeOrderDao.getByOderId(orderPayRequest.getOrderNo());
        if (ObjectUtil.isNull(storeOrder)) {
            throw new TalkException("订单不存在");
        }
        if (storeOrder.getIsDel()) {
            throw new TalkException("订单已被删除");
        }
        if (storeOrder.getPaid()) {
            throw new TalkException("订单已支付");
        }
        User user = userService.getById(storeOrder.getUid());
        if (ObjectUtil.isNull(user)) throw new TalkException("用户不存在");
        // 获取用户openid专用
        UserToken userToken = new UserToken();

        // 判断订单是否还是之前的支付类型
        if (!storeOrder.getPayType().equals(orderPayRequest.getPayType())) {
            // 更换支付类型
            storeOrder.setPayType(orderPayRequest.getPayType());
            boolean changePayType = storeOrderDao.updateById(storeOrder) >= 1;
            if (!changePayType) {
                throw new TalkException("变更订单支付类型失败!");
            }
        }

        // 余额支付
        if (orderPayRequest.getPayType().equals(PayConstants.PAY_TYPE_YUE)) {
            if (user.getNowMoney().compareTo(storeOrder.getPayPrice()) < 0) {
                throw new TalkException("用户余额不足");
            }
            storeOrder.setIsChannel(3);
        }

        if (orderPayRequest.getPayType().equals(PayConstants.PAY_TYPE_WE_CHAT)) {
            switch (orderPayRequest.getPayChannel()){
                // h5、app
                case Constants.PAY_CHANNEL_WE_CHAT_H5:
                case Constants.PAY_CHANNEL_WE_CHAT_APP:
                    storeOrder.setIsChannel(2);
                    userToken.setToken("");
                    break;
                case Constants.PAY_CHANNEL_WE_CHAT_PUBLIC:// 公众号
                    storeOrder.setIsChannel(0);
                    userToken = userTokenService.getTokenByUserId(user.getUid(), 1);
                    break;
                case Constants.PAY_CHANNEL_WE_CHAT_PROGRAM:// 小程序
                    storeOrder.setIsChannel(1);
                    userToken = userTokenService.getTokenByUserId(user.getUid(), 2);
                    break;
                default:
                    break;
            }
        }

        OrderPayResultResponse response = new OrderPayResultResponse();
        response.setOrderNo(storeOrder.getOrderId());
        response.setPayType(storeOrder.getPayType());
        // 0元付
        if (storeOrder.getPayPrice().compareTo(BigDecimal.ZERO) <= 0) {
            yuePay(storeOrder);
            response.setPayType(PayConstants.PAY_TYPE_YUE);
            return response;
        }
        // 微信支付，调用微信预下单，返回拉起微信支付需要的信息
        if (storeOrder.getPayType().equals(PayConstants.PAY_TYPE_WE_CHAT)) {
            String openId = userToken.getToken();
            // 预下单
            Map<String, String> map = weChatPayService.wxPay(user.getUid(), openId, orderPayRequest.getPayChannel(), storeOrder.getOrderId(), storeOrder.getPayPrice(), ip, "order");
            WxPayJsResultVo wxPayConfig = new WxPayJsResultVo();
            wxPayConfig.setAppId(map.get("appId"));
            wxPayConfig.setNonceStr(map.get("nonceStr"));
            wxPayConfig.setPackages(map.get("package"));
            wxPayConfig.setSignType(map.get("signType"));
            wxPayConfig.setTimeStamp(map.get("timeStamp"));
            wxPayConfig.setPaySign(map.get("paySign"));
            wxPayConfig.setPrepayId(map.get("prepayid"));
            wxPayConfig.setMchId(map.get("partnerid"));
            wxPayConfig.setWebUrl(map.get("web_url"));
            storeOrderDao.updateById(storeOrder);

            response.setJsConfig(wxPayConfig);
            return response;
        }
        // 余额支付
        if (storeOrder.getPayType().equals(PayConstants.PAY_TYPE_YUE)) {
            Boolean yueBoolean = yuePay(storeOrder);
            return response;
        }
        // 线下支付
        if (storeOrder.getPayType().equals(PayConstants.PAY_TYPE_OFFLINE)) {
            throw new TalkException("暂时不支持线下支付");
        }
        return response;
    }

    /**
     * 余额支付
     * @param storeOrder 订单
     * @return boolean Boolean
     */
    private boolean yuePay(StoreOrder storeOrder) {

        // 用户余额扣除
        User user = userService.getById(storeOrder.getUid());
        if (ObjectUtil.isNull(user)) throw new TalkException("用户不存在");
        if (user.getNowMoney().compareTo(storeOrder.getPayPrice()) < 0) {
            throw new TalkException("用户余额不足");
        }
        if (user.getIntegral() < storeOrder.getUseIntegral()) {
            throw new TalkException("用户积分不足");
        }
        storeOrder.setPaid(true);
        storeOrder.setPayTime(DateUtil.getNowTime());
        Boolean execute = transactionTemplate.execute(e -> {
            // 订单修改
            storeOrderDao.updateById(storeOrder);
            // 这里只扣除金额，账单记录在task中处理
            userService.updateNowMoney(user, storeOrder.getPayPrice(), "sub");
            // 扣除积分
            if (storeOrder.getUseIntegral() > 0) {
                userService.updateIntegral(user, storeOrder.getUseIntegral(), "sub");
            }
            // 添加支付成功redis队列
            redisUtil.lPush(net.sk.china.front.constants.Constants.ORDER_TASK_PAY_SUCCESS_AFTER, storeOrder.getOrderId());
            return Boolean.TRUE;
        });
        if (!execute) {
            throw new TalkException("余额支付订单失败");
        }
        return true;
    }
}
